package org.example.GirdFind;

import org.example.bean.Cord;
import org.example.bean.JsonGamePos;

import java.util.*;

/**
 * @ClassName GameGirdMap
 * @Description TODO
 * @Author hongjun
 * @Date 2024/5/26 下午6:31
 * @Version 1.0
 */
public class GameGirdMap {
    private final float GIRDRADIO = 10.0F;
    //地图长宽
    private int weight;
    private int height;
    //阻挡哈希稀疏矩阵
    private final Map<Integer, Set<Integer>> blocks;
    //格子
    private Gird[][] girds;
    //地图格子长宽
    private int girdWeight;
    private int girdHeight;
    //节点对象池
    private GirdNodePool nodePool;

    //8个方向
    private final int[][] moves = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}, {-1, -1}, {-1, 1}, {1, -1}, {1, 1}};

    public GameGirdMap(int maxX, int maxY) {
        weight = maxX;
        height = maxY;
        girdWeight = maxX ;
        girdHeight = maxY ;
        girds = new Gird[maxX][maxY];
        for (int i = 0; i < girdWeight; i++) {
            for (int j = 0; j < girdHeight; j++) {
                girds[i][j] = new Gird(i, j, 0);
            }
        }
        //初始化对象池
        nodePool = new GirdNodePool(10000, weight * height * 10);
        this.blocks = new HashMap<>();
    }

    public Gird[][] getGirds() {
        return girds;
    }

    public GirdNodePool getNodePool() {
        return nodePool;
    }

    public int getWeight() {
        return weight;
    }

    public int getHeight() {
        return height;
    }


    /**
     * 更新所有格子
     */
    public void updateAllGird() {
        for (Gird[] girdList : girds) {
            for (Gird gird : girdList) {
                int x = gird.getX() * 10;
                int y = gird.getY() * 10;
                int block = 0;
                for (int i = 0; i < 10; i++) {
                    for (int j = 0; j < 10; j++) {
                        if (isBlocked(x + i, y + j)) {
                            block++;
                        }
                    }
                }
                gird.setBlock(block);
            }
        }
        girds[87][39].setType(1);
        girds[88][39].setType(1);
    }

    /**
     * 更新单个格子
     */
    public void updateGird(Gird gird) {
        int x = gird.getX() * 10;
        int y = gird.getY() * 10;
        int block = 0;
        for (int i = 0; i < x; i++) {
            for (int j = 0; j < y; j++) {
                if (isBlocked(x, y)) {
                    block++;
                }
            }
        }
        gird.setBlock(block);
    }

    /**
     * 获取给定节点的所有邻居节点。
     *
     * @param node 给定节点
     * @return 邻居节点列表
     */
    public List<GirdNode> getNeighbors(GirdNode node) {
        List<GirdNode> neighbors = new ArrayList<>();
        for (int i = 0; i < moves.length; i++) {
            int[] move = moves[i];
            int nextX = node.getX() + move[0];
            int nextY = node.getY() + move[1];
            if (nextX >= 0 && nextY >= 0 && nextX < weight && nextY < height && !isBlocked(nextX, nextY)) {
                GirdNode acquire = nodePool.acquire(nextX, nextY, i + 1);
                neighbors.add(acquire);
            }
        }
        return neighbors;
    }

    /**
     * 获取邻居格子
     *
     * @param node
     * @param
     * @return
     */
    public List<Gird> getGirdNeighbors(GirdNode node) {
        List<Gird> neighbors = new ArrayList<>();
        int nodeX = node.getX() * 10 + 5;
        int nodeY = node.getY() * 10 + 5;
        for (int i = 0; i < moves.length; i++) {
            int[] move = moves[i];
            int nextX = node.getX() + move[0];
            int nextY = node.getY() + move[1];
            int x = nextX * 10 + 5;
            int y = nextY * 10 + 5;
            if (isBlocked(x, y)) {
                continue;
            }
            if (!canConnectStraight(nodeX, nodeY, x, y)) {
                continue;
            }
            if (nextX >= 0 && nextY >= 0 && nextX < girdWeight && nextY < girdHeight) {
                Gird gird = girds[nextX][nextY];
                /*if (gird.getBlock() < 10 ) {
                    neighbors.add(gird);
                }
                if (gird.getType() == 1){
                    neighbors.add(gird);
                }*/
                neighbors.add(gird);
            }
        }
        return neighbors;
    }

    /**
     * 添加hash阻挡
     *
     * @param x
     * @param y
     */
    public void addBlock(int x, int y) {
        if (x >= 0 && x < weight && y >= 0 && y < height) {
            // 获取x行对应的列集合（如果不存在则创建）
            Set<Integer> columns = blocks.computeIfAbsent(x, k -> new HashSet<>());
            // 添加y列到集合中
            columns.add(y);
        }
    }

    /**
     * 移除hash阻挡
     *
     * @param x
     * @param y
     */
    public void removeBlock(int x, int y) {
        if (x >= 0 && x < weight && y >= 0 && y < height) {
            if (blocks.containsKey(x)) {
                Set<Integer> columns = blocks.get(x);
                columns.remove(y);
            }
        }
    }

    /**
     * 检查是否为阻挡
     *
     * @param x
     * @param y
     * @return
     */
    public boolean isBlocked(int x, int y) {
        // 检查x行是否存在，并且y列是否在该行的集合中
        Set<Integer> columns = blocks.get(x);
        return columns != null && columns.contains(y);
    }

    /**
     * 将游戏坐标转换为节点Node。
     *
     * @param gameX 游戏内的X坐标
     * @param gameY 游戏内的Y坐标
     * @return 一个包含地图数组索引的整数数组，第一个元素是X索引，第二个元素是Y索引
     */
    public GirdNode gameToMapIndex(float gameX, float gameY) {
        return nodePool.acquire((int) (gameX * GIRDRADIO), (int) (gameY * GIRDRADIO), 0);
    }

    /**
     * 将游戏坐标转换为大格子Node。
     *
     * @return 一个包含地图数组索引的整数数组，第一个元素是X索引，第二个元素是Y索引
     */
    public GirdNode JsonGamePosToMapGirdIndex(JsonGamePos pos) {
        return nodePool.acquire((int) (pos.getX()), (int) (pos.getZ()), 0);
    }


    /**
     * 优化路径，通过去除不必要的节点来简化路径。
     *
     * @param path 原始路径
     * @return 优化后的路径
     */
    public List<JsonGamePos> optimizePath(List<Cord> path) {
        if (path == null || path.isEmpty()) {
            return null;
        }
        List<JsonGamePos> optimizedPathPositions = new ArrayList<>();
        if (path.size() >= 3) {
            List<Cord> optimizedPath = new ArrayList<>();
            // 起点总是加入优化路径
            optimizedPath.add(path.get(0));
            Cord lastOptimizedNode = path.get(0);
            for (int i = 1; i < path.size() - 1; i++) {
                Cord currentNode = path.get(i);
                Cord nextNode = path.get(i + 1);
                // 检查当前节点是否可以与下一个节点直线相连而不穿过障碍物
                if (!canConnectStraight(lastOptimizedNode, nextNode)) {
                    // 如果不能直线相连，则添加当前节点到优化路径中
                    optimizedPath.add(currentNode);
                    lastOptimizedNode = currentNode;
                }
            }
            // 终点总是加入优化路径
            optimizedPath.add(path.get(path.size() - 1));

            // 将Cord路径转换为JsonGamePos列表
            for (Cord cord : optimizedPath) {
                optimizedPathPositions.add(mapToGameCoords(cord.getX(), cord.getY()));
            }
        } else {
            // 如果路径长度小于3，则直接转换并返回
            for (Cord cord : path) {
                optimizedPathPositions.add(mapToGameCoords(cord.getX(), cord.getY()));
            }
        }
        return optimizedPathPositions;
    }

    /**
     * 基于坐标的优化路径
     *
     * @param path
     */
    public List<JsonGamePos> optimizeJsonGamePosPath(List<JsonGamePos> path) {
        if (path == null || path.isEmpty()) {
            return null;
        }
        List<JsonGamePos> optimizedPathPositions = new ArrayList<>();
        if (path.size() >= 3) {
            List<Cord> optimizedPath = new ArrayList<>();
            // 起点总是加入优化路径
            optimizedPath.add(gamePosToCord(path.get(0)));
            Cord lastOptimizedCord = gamePosToCord(path.get(0));
            for (int i = 1; i < path.size() - 1; i++) {
                Cord currentNode = gamePosToCord(path.get(i));
                Cord nextNode = gamePosToCord(path.get(i + 1));
                // 检查当前节点是否可以与下一个节点直线相连而不穿过障碍物
                if (!canConnectStraight(lastOptimizedCord, nextNode)) {
                    // 如果不能直线相连，则添加当前节点到优化路径中
                    optimizedPath.add(currentNode);
                    lastOptimizedCord = currentNode;
                }
            }
            // 终点总是加入优化路径
            optimizedPath.add(gamePosToCord(path.get(path.size() - 1)));

            // 将Cord路径转换为JsonGamePos列表
            for (Cord cord : optimizedPath) {
                optimizedPathPositions.add(mapToGameCoords(cord.getX(), cord.getY()));
            }
        } else {
            // 如果路径长度小于3，则直接返回
            return path;
        }
        return optimizedPathPositions;
    }


    /**
     * 判断2个节点是否可以直线相连
     *
     * @param start
     * @param end
     * @return
     */
    private boolean canConnectStraight(Cord start, Cord end) {
        return canConnectStraight(start.getX(), start.getY(), end.getX(), end.getY());
    }

    /**
     * 检查两个节点是否可以直线相连而不穿过障碍物。
     *
     * @param x1 起点x
     * @param y1 起点y
     * @param x2 终点x
     * @param y2 终点y
     * @return 如果两点之间可以直线相连并且没有障碍物则返回true，否则返回false
     */
    public boolean canConnectStraight(int x1, int y1, int x2, int y2) {
        int dx = Math.abs(x2 - x1);
        int dy = Math.abs(y2 - y1);
        int sx = (x1 < x2) ? 1 : -1;
        int sy = (y1 < y2) ? 1 : -1;
        int err = dx - dy;
        while (true) {
            if (isInBounds(x1, y1) && isBlocked(x1, y1)) {
                // 路径上有障碍物
                return false;
            }
            if (x1 == x2 && y1 == y2) {
                break; // 到达终点
            }
            int e2 = err * 2;
            if (e2 > -dy) {
                err -= dy;
                x1 += sx;
            }
            if (e2 < dx) {
                err += dx;
                y1 += sy;
            }
        }
        // 路径上无障碍物
        return true;
    }

    /**
     * 检查给定的坐标是否在网格的边界内。
     *
     * @param x 坐标x
     * @param y 坐标y
     * @return 如果坐标在网格内则返回true，否则返回false
     */
    public boolean isInBounds(int x, int y) {
        return x >= 0 && x < weight && y >= 0 && y < height;
    }

    /**
     * 将地图节点坐标转换回游戏内的坐标。
     *
     * @param mapX 地图数组的X索引
     * @param mapY 地图数组的Y索引
     * @return 一个包含游戏内坐标的整数数组，第一个元素是X坐标，第二个元素是Y坐标
     */
    public JsonGamePos mapToGameCoords(int mapX, int mapY) {
        return new JsonGamePos((float) mapX / GIRDRADIO, (float) mapY / GIRDRADIO);
    }

    /**
     * 根据给定的当前节点，重建并返回从起点到该节点的大格子路径。
     *
     * @param currentNode 当前节点
     * @return 从起点到当前节点的路径
     */
    public List<Gird> reconstructGird(GirdNode currentNode) {
        List<Gird> path = new ArrayList<>();
        int depth = 0;
        while (currentNode != null) {
            Gird gird = girds[currentNode.getX()][currentNode.getY()];
            if (gird.getBlock() > 0 || depth != currentNode.getDirection() || depth == 0 || currentNode.getParent() == null) {
                path.add(girds[currentNode.getX()][currentNode.getY()]);
                depth = currentNode.getDirection();
            }
            currentNode = currentNode.getParent();
        }
        Collections.reverse(path);
        return path;
    }


    /**
     * 地图坐标转为寻路节点
     *
     * @param gamePos
     * @return
     */
    public Cord gamePosToCord(JsonGamePos gamePos) {
        return new Cord((int) (gamePos.getX() * GIRDRADIO), (int) (gamePos.getZ() * GIRDRADIO));
    }
}
